package com.naiterui.ehp.bs.im.service.message.handler;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import com.naiterui.ehp.bp.bo.doctor.RecomUserInfoBO;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.support.security.HeaderConstant;
import com.naiterui.ehp.bs.im.config.MediaFileConfig;
import com.naiterui.ehp.bs.im.config.MessageConfig;
import com.naiterui.ehp.bs.im.exception.ExceptionCodes;
import com.naiterui.ehp.bs.im.exception.NaiteruiIMException;
import com.naiterui.ehp.bs.im.feign.DoctorFeginClient;
import com.naiterui.ehp.bs.im.network.protocl.Message;
import com.naiterui.ehp.bs.im.network.protocl.RecommandDrug;
import com.naiterui.ehp.bs.im.network.protocl.Session;
import com.naiterui.ehp.bs.im.network.protocl.SessionDetails;
import com.naiterui.ehp.bs.im.repository.file.MediaFileHandler;
import com.naiterui.ehp.bs.im.repository.mongo.MessageDao;
import com.naiterui.ehp.bs.im.repository.mongo.Page;
import com.naiterui.ehp.bs.im.repository.mongo.SessionDao;
import com.naiterui.ehp.bs.im.utils.Constants;
import com.naiterui.ehp.bs.im.utils.DateUtils;
import com.naiterui.ehp.bs.im.vo.ImgTimeGroupVO;
import com.naiterui.ehp.bs.im.vo.MediaBatchUploadVO;
import com.naiterui.ehp.bs.im.vo.MediaUploadVO;
import com.naiterui.ehp.bs.im.vo.MessageHandleResult;
import com.naiterui.ehp.bs.im.vo.MessageTimeGroupVO;
import com.naiterui.ehp.bs.im.vo.PageVO;
import com.naiterui.ehp.bs.im.vo.QueryHistoryCondVO;

/**
 * 消息处理器包装类
 */
@Service
public class MessageHandler {

    /**
     * LOGGER
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageHandler.class);

    final
    SessionDao sessionDao;
    private final AbstractMessageHandler patientMsgHandler;
    private final AbstractMessageHandler doctorMsgHandler;
    private final MessageDao messageDao;
    private final MediaFileConfig mediaFileConfig;
    private final MessageConfig messageConfig;
    private final MediaFileHandler mediaFileHandler;
    private final DoctorFeginClient doctorFeginClient;

    public MessageHandler(SessionDao sessionDao, @Qualifier(value = "patientMessageHandler") AbstractMessageHandler patientMsgHandler, @Qualifier(value = "doctorMessageHandler") AbstractMessageHandler doctorMsgHandler, MessageDao messageDao, MediaFileConfig mediaFileConfig,
                          MessageConfig messageConfig, MediaFileHandler mediaFileHandler, DoctorFeginClient doctorFeginClient) {
        this.sessionDao = sessionDao;
        this.patientMsgHandler = patientMsgHandler;
        this.doctorMsgHandler = doctorMsgHandler;
        this.messageDao = messageDao;
        this.mediaFileConfig = mediaFileConfig;
        this.messageConfig = messageConfig;
        this.mediaFileHandler = mediaFileHandler;
        this.doctorFeginClient = doctorFeginClient;
    }

    /**
     * 处理消息
     */
    public MessageHandleResult handleMessage(Message<?> messageBean) throws NaiteruiIMException {

        MessageHandleResult handleResult = new MessageHandleResult();
        // 根据消息的发送对象不同进行各自逻辑处理
        if (messageBean.getRelation() == Constants.MESSAGE_RESOURCE_PATIENT) {
            messageBean = this.patientMsgHandler.handleMessage(messageBean);
        } else {
            messageBean = this.doctorMsgHandler.handleMessage(messageBean);
        }

        // 推荐购药消息，消息处理结果添加recommendId(V2.5.0 重复推荐需求)
        if (messageBean.getType() == Constants.MESSAGE_TYPE_DRUG) {
            RecommandDrug recomDrug = (RecommandDrug) messageBean.getContent();
            handleResult.setSerialNumber(recomDrug.getSerialNumber());
            handleResult.setRecommandId(recomDrug.getRecommandId());
            handleResult.setRecommandStatus(recomDrug.getRecommandStatus());
        }

        handleResult.setSendTime(messageBean.getSendTime());
        handleResult.setConsultPayType(messageBean.getSession() == null ? null : messageBean.getSession().getConsultPayType());

        return handleResult;
    }

    /**
     * 保存多媒体文件
     */
    public String persistentMediaFile(MediaUploadVO mediaUpload, MultipartFile file) throws NaiteruiIMException {
        // 文件下载的完整地址
        String fileDownloadURL = null;
        try {
            if (mediaUpload.getType() == Constants.MESSAGE_TYPE_MEDIA_IMG) {
                fileDownloadURL = this.mediaFileHandler.saveImage(mediaUpload.getFromId(), mediaUpload.getToId(), file.getBytes());
            } else if (mediaUpload.getType() == Constants.MESSAGE_TYPE_MEDIA_AUDIO) {
                fileDownloadURL = this.mediaFileHandler.saveRadio(mediaUpload.getOrigin(), mediaUpload.getPlatform(), mediaUpload.getFromId(),
                        mediaUpload.getToId(), file.getBytes());
            } else if (mediaUpload.getType() == Constants.MESSAGE_TYPE_MEDIA_VIDEO) {
                fileDownloadURL = this.mediaFileHandler.saveVideo(mediaUpload.getFromId(), mediaUpload.getToId(), file.getBytes());
            }
            return fileDownloadURL;
        } catch (Exception e) {
            LOGGER.error("上传文件错误", e);
            throw new NaiteruiIMException(ExceptionCodes.MEDIA_UPLOAD_FAILD, e);
        }
    }

    /**
     * MessageHandler.persistentMediaFile()
     */
    public String persistentMediaFile(MediaBatchUploadVO mediaBatchUpload, MultipartFile[] files, int index) throws NaiteruiIMException {
        // 文件下载的完整地址
        String fileDownloadURL = null;
        int mediaType = mediaBatchUpload.getTypes().get(index);
        MultipartFile file = files[index];
        try {
            if (mediaType == Constants.MESSAGE_TYPE_MEDIA_IMG) {
                fileDownloadURL = this.mediaFileHandler.saveImage(mediaBatchUpload.getFromId(), mediaBatchUpload.getToId(), file.getBytes());
            } else if (mediaType == Constants.MESSAGE_TYPE_MEDIA_AUDIO) {
                fileDownloadURL = this.mediaFileHandler.saveRadio(mediaBatchUpload.getOrigin(), mediaBatchUpload.getPlatform(),
                        mediaBatchUpload.getFromId(), mediaBatchUpload.getToId(), file.getBytes());
            } else if (mediaType == Constants.MESSAGE_TYPE_MEDIA_VIDEO) {
                fileDownloadURL = this.mediaFileHandler.saveVideo(mediaBatchUpload.getFromId(), mediaBatchUpload.getToId(), file.getBytes());
            }
            return fileDownloadURL;
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw new NaiteruiIMException(ExceptionCodes.MEDIA_UPLOAD_FAILD);
        }
    }

    /**
     * 根据分页获取历史记录
     */
    public PageVO getChatHistory(int origin, QueryHistoryCondVO vo) throws BusinessException {

        Long doctorId = vo.getToId();
        Long patientId = vo.getFromId();
        RecomUserInfoBO recomUserInfoBO = doctorFeginClient.getUserInfoForRecom(doctorId, patientId);

        // realtion(1:医生端；2:患者 端 )
        int relation = this.convertRelation(origin);

        // 获取可见消息类型
        Integer visualType = this.genVisualTyep(origin);

        // 获取总记录数 如果是微信端请求的话不需要传递时间
        long totalCount = this.messageDao.queryCount(relation, vo.getBeginTime(), vo.getFromId(), vo.getToId());

        // 总页数
        long totalPage = totalCount / vo.getNum();
        if (totalCount % vo.getNum() > 0) {
            totalPage++;
        }

        // 排序条件
        String orderby = (vo.getOrderby() == null || vo.getOrderby() == QueryHistoryCondVO.ASC) ? Page.ASC : Page.DESC;

        // 获取指定页数的数据记录
        List<Message> messages = this.messageDao.pageQuery(relation, vo.getBeginTime(), visualType, vo.getFromId(), vo.getToId(), vo.getPage(),
                vo.getNum(), orderby);

        // 构建分页对象
        PageVO pageVO = new PageVO();
        pageVO.setPageNo(vo.getPage());
        pageVO.setPageSize(vo.getNum());
        pageVO.setResult(messages);
        pageVO.setTotalCount((int) totalCount);
        pageVO.setTotalPage((int) totalPage);
        pageVO.setOrderBy(orderby);
        pageVO.setBaseUrl(this.mediaFileConfig.getServerDomain());

        /*
         * 查看消息记录采用的下滑更新的方式
         * 因此需要对倒序分页获得的每页的结果集再进行一次倒序
         * 如果是微信，由于PHP使用模板不支持倒序，此处需要服务端进行处理
         */
        if (origin == HeaderConstant.HEADER_ORIGIN_PATIENT_WX) {
            // List倒序
            Collections.reverse(messages);
        }

        // 表示需要服务器进行时间分组
        if (vo.isTimegroup()) {
            List<MessageTimeGroupVO> messageTimeGroupVos = this.getMessageByTimeGroup(messages, recomUserInfoBO);
            pageVO.setResult(messageTimeGroupVos);
        }

        return pageVO;
    }

    /**
     * 获取用户的图片上传列表
     */
    public PageVO<ImgTimeGroupVO> getImageUploadHistory(Long patientId, Long doctorId, Integer page, Integer num) throws NaiteruiIMException {
        PageVO<ImgTimeGroupVO> pageVO = this.messageDao.getImageUploadHistory(patientId, doctorId, page, num);
        pageVO.setBaseUrl(this.mediaFileConfig.getServerDomain());
        return pageVO;
    }

    /************************************* 私有方法 *****************************************/

    /**
     * 将消息转换为时间分组
     */
    private List<MessageTimeGroupVO> getMessageByTimeGroup(List<Message> messages, RecomUserInfoBO recomUserInfoBO) {

        // 返回值
        List<MessageTimeGroupVO> results = new ArrayList<>();
        if (messages.isEmpty()) {
            return results;
        }

        // 时间分组对应消息的Map
        Map<Long, MessageTimeGroupVO> map = new LinkedHashMap<>();

        // 获取基准时间
        Long lastTime = DateUtils.getTimeInMillisByMinute(messages.get(0).getSendTime());

        // 当前发送消息的用户
        Long userId = messages.get(0).getFrom().getId();

        // 遍历数据根据时间进行分组
        for (Iterator<Message> iter = messages.iterator(); iter.hasNext(); ) {

            // 消息内容
            Message<?> message = iter.next();

            // 获取当前消息与基准消息的差值，如果差值大于基准值或者发送人发生变化则添加新分组
            if (message.getFrom().getId().compareTo(userId) != 0
                    || DateUtils.isTimeIntervalInBase(this.messageConfig.getGroupTimeInterval(), message.getSendTime(), lastTime)) {

                // 根据基准时间获取分组信息
                MessageTimeGroupVO groupVO = map.get(lastTime);

                // 分组下的消息列表
                List<Message<?>> messageBeanList = null;

                // 构建分组
                if (groupVO == null) {
                    groupVO = new MessageTimeGroupVO();
                    this.addUserInfo(recomUserInfoBO, groupVO);
                    messageBeanList = new ArrayList<>();
                    groupVO.setMessages(messageBeanList);
                    groupVO.setTimeGroup(message.getSendTime());
                    map.put(lastTime, groupVO);
                } else {
                    messageBeanList = groupVO.getMessages();
                }

                // 将数据加入分组集合
                messageBeanList.add(message);

            } else {

                // 更新基准时间和用户
                lastTime = DateUtils.getTimeInMillisByMinute(message.getSendTime());
                userId = message.getFrom().getId();

                // 创建新分组
                MessageTimeGroupVO groupVO = new MessageTimeGroupVO();
                this.addUserInfo(recomUserInfoBO, groupVO);
                List<Message<?>> messageBeanList = new ArrayList<>();
                groupVO.setMessages(messageBeanList);
                groupVO.setTimeGroup(message.getSendTime());
                messageBeanList.add(message);
                map.put(lastTime, groupVO);
            }
        }

        // 转换为List
        Collection<MessageTimeGroupVO> timeGroups = map.values();
        results.addAll(timeGroups);

        return results;
    }

    private void addUserInfo(RecomUserInfoBO recomUserInfoBO, MessageTimeGroupVO groupVO) {
        if (recomUserInfoBO != null && recomUserInfoBO.getDoctor() != null && recomUserInfoBO.getPatient() != null) {
            groupVO.setDoctorName(recomUserInfoBO.getDoctor().getName());
            groupVO.setDoctorTitle(recomUserInfoBO.getDoctor().getTitle());
            groupVO.setHospitalName(recomUserInfoBO.getDoctor().getHospitalName());
            groupVO.setDoctorHeadurl(recomUserInfoBO.getDoctor().getHeadUrl());
            groupVO.setPatientName(recomUserInfoBO.getPatient().getName());
            groupVO.setPatientHeadurl(recomUserInfoBO.getPatient().getHeadUrl());
        }
    }

    /**
     * 查询session中的消息列表
     *
     * @param sessionId
     * @param pageNo
     * @param pageSize
     * @param endTime
     * @param origin
     *
     * @return
     *
     * @since 1.0.0
     */
    public PageVO<Message> findMessageBySession(String sessionId, Integer pageNo, Integer pageSize, Long endTime, Integer origin, Long doctorId, Long patientId) {

        PageVO<Message> pageVO = new PageVO<>();
        pageVO.setPageNo(pageNo);
        pageVO.setPageSize(pageSize);
        pageVO.setOrderBy(Page.DESC);
        pageVO.setBaseUrl(this.mediaFileConfig.getServerDomain());
        if(StringUtils.isEmpty(sessionId) && null == patientId){
            pageVO.setResult(Collections.emptyList());
            return pageVO;
        }
        Long fromId;
        Long toId;
        Integer relation;
        Long msgLteTime;
        if (StringUtils.isEmpty(sessionId)) {
            fromId = doctorId;
            toId = patientId;
            relation = 1;

        } else {
            // 根据sessionId获取会话详情
            SessionDetails session = this.sessionDao.findSessionById(sessionId);
            fromId = session.getFromId();
            toId = session.getToId();
            relation = session.getRelation();
//            if(null == endTime){
//                // 没有会话时医生主动发的消息不创建会话，所以取当前时间
//                msgLteTime = System.currentTimeMillis();
//            } else {
//                // 消息查询lte时间点
//                msgLteTime = null != session.getEndTime() && session.getEndTime().longValue() > 0
//                        ? session.getEndTime().compareTo(endTime) > 0 ? endTime : session.getEndTime()
//                        : endTime;
//            }
        }
        // 没有会话时医生主动发的消息不创建会话，所以取当前时间
        if(null == endTime){
            msgLteTime = System.currentTimeMillis();
        } else {
            msgLteTime = endTime;
        }
        // 获取可见消息类型
        Integer visualType = this.genVisualTyep(origin);
        // 获取对应消息记录
        List<Message> result = this.messageDao.pageQueryCross(fromId, toId, relation, msgLteTime, visualType, pageNo, pageSize, Page.DESC);

        List<SessionDetails> sessionList = this.sessionDao.findSessionByTime(fromId, toId, relation, msgLteTime,null, pageNo, 10);
        if (!CollectionUtils.isEmpty(sessionList)) {
            int curIndex = 0;
            for (Message tempMessage : result) {
                Session tempSession = sessionList.get(curIndex);
                if (tempMessage.getSendTime().compareTo(tempSession.getBeginTime()) < 0) {
                    curIndex++;
                    if (curIndex + 1 > sessionList.size()) {
                        sessionList = this.sessionDao.findSessionByTime(fromId, toId, relation, msgLteTime, null, pageNo, 10);
                        curIndex = 0;
                    }
                    tempSession = sessionList.get(curIndex);
                }
                Session sessionInfo = new Session();
                sessionInfo.setSessionId(tempSession.getSessionId());
                sessionInfo.setConsultPayType(tempSession.getConsultPayType());
                tempMessage.setSession(sessionInfo);
            }
        }
        pageVO.setResult(result);
        return pageVO;
    }

    /**
     * 根据应用来源，获取可见消息类型
     */
    private Integer genVisualTyep(Integer origin) {

        Integer visualType = null;

        switch (origin) {
            case HeaderConstant.HEADER_ORIGIN_PATIENT_APP:
                visualType = Message.MESSAGE_VISUAL_TYPE_PATIENT;
                break;

            case HeaderConstant.HEADER_ORIGIN_DOCTOR_APP:
                visualType = Message.MESSAGE_VISUAL_TYPE_DOCTOR;
                break;

            case HeaderConstant.HEADER_ORIGIN_PATIENT_WX:
                visualType = Message.MESSAGE_VISUAL_TYPE_PATIENT;
                break;

            case HeaderConstant.HEADER_ORIGIN_YUNHOSPITAL_PC:
                visualType = Message.MESSAGE_VISUAL_TYPE_ALL;
                break;

            default:
                visualType = Message.MESSAGE_VISUAL_TYPE_ALL;
                break;
        }
        return visualType;
    }

    /**
     * MessageHandler.convertRelation()
     */
    private int convertRelation(int origin) throws BusinessException {
        Integer relationType = null;

        switch (origin) {
            case HeaderConstant.HEADER_ORIGIN_PATIENT_APP:
            case HeaderConstant.HEADER_ORIGIN_PATIENT_WX:
                relationType = Constants.MESSAGE_RESOURCE_PATIENT;
                break;

            case HeaderConstant.HEADER_ORIGIN_DOCTOR_APP:
                relationType = Constants.MESSAGE_RESOURCE_DOCTOR;
                break;

            case HeaderConstant.HEADER_ORIGIN_YUNHOSPITAL_PC:
                throw new BusinessException(ExceptionCodes.PARAM_ERROR);

            default:
                throw new BusinessException(ExceptionCodes.PARAM_ERROR);
        }
        return relationType;
    }

}
